#ifndef __HEMUIRCode_H__
#define __HEMUIRCode_H__

#include <iostream>
//#include <map>
#include <vector>
#include <cassert>
#include <cstddef>
//#include "HEMUHdb.h"


/*
 * HEMU IR Base Type
 */
enum class HEMUIRBtype {
	UNKNOWN = 0, VOID, I32, I8, FLOAT, DOUBLE, POINTER, LABEL
};


/*
 * HEMU IR Value
 */
typedef union {
	int64_t i64_val;	//line_no
	int32_t i32_val;
	int8_t i8_val;
	float float_val;
	double double_val;
	char* ptr_val;
}HEMUIRVal;


/*
 * HEMU IR variable
 */
class HEMUIRVar {
public:
	HEMUIRBtype btype;
	HEMUIRVal val;
public:
	//HEMUIRVar();
	//HEMUIRVar(HEMUIRBtype _bt, HEMUIRVal _val);
	HEMUIRVar operator+(const HEMUIRVar& var);
	HEMUIRVar operator-(const HEMUIRVar& var);
	HEMUIRVar operator*(const HEMUIRVar& var);
	HEMUIRVar operator/(const HEMUIRVar& var);
	HEMUIRVar operator%(const HEMUIRVar& var);
	HEMUIRVar operator<<(const HEMUIRVar& var);
	HEMUIRVar operator&(const HEMUIRVar& var);
	HEMUIRVar operator|(const HEMUIRVar& var);
	HEMUIRVar operator^(const HEMUIRVar& var);
	void print_var();


};


/* HEMU IR Type
 * mark the type of pointer of a global variable
 */
struct HEMUIRType {
	// array length
	size_t length;
	HEMUIRBtype base_type;
	HEMUIRType(uint32_t _length = 0, HEMUIRBtype _base_type = HEMUIRBtype::UNKNOWN) {
		length = _length;
		base_type = _base_type;
	}
};


/* HEMU IR Operand
 * only three types of operand: temporary, imm, global var
 */
struct HEMUIROpnd {
	enum { TEMP, IMM, GLOVAR } tp;
	union {
		size_t temp_idx;
		HEMUIRVar imm;
		size_t glovar_idx;
	};
	
	bool valid_opnd();
};



/* HEMU IR Global
 * all HEMU IR Global var need to be ptr
 */
struct HEMUIRGlovar {
	std::string glo_name;
	HEMUIRType tp;
	char* glo_var_ptr;
};

/* HEMU IR Global Table
 * search the value by the name
 * return SIZE_MAX when false
 */
class HEMUIRGloTb {
private:
	std::vector<HEMUIRGlovar>glo_tb;
public:
	HEMUIRGlovar get_glo(size_t idx);
	size_t add_glo_tb(HEMUIRGlovar glo_var);
	size_t search_glo(std::string name);
};

/* HEMU IR Temporary Table
 * search the value by the idx
 * return -1 when false
 */
class HEMUIRTempTb {
private:
	std::vector<HEMUIRVar> temp_tb;

public:
	HEMUIRVar get_temp_val(size_t temp_idx);
	size_t get_size();
	size_t set_temp_tb(size_t temp_idx, const HEMUIRVar& val);
};


/*
 * HEMU IR operations
 */
enum class HEMUIROper {
	NOP,			// empty line
	RET, BRc, BRu, // conditional/unconditional
	FNEG,
	ADD, FADD, SUB, FSUB, MUL, FMUL, UDIV, SDIV, FDIV, UREM, SREM,
	SHL, LSHR, ASHR, AND, OR, XOR,
	ALLOCA, LOAD, STORE,
	ICMP, FCMP,
	CALL,
	LABEL
};

/*
 * HEMU IR Function Entry
 */
struct HEMUIRFEntry {
	std::string name;
	size_t lineno_start;
	size_t lineno_end;
	HEMUIRTempTb temp_tb;
	HEMUIRBtype ret_tp;
	std::vector <HEMUIROpnd> fargs;
};

/*
 * HEMU IR Function Table
 */
class HEMUIRFTb {
public:
	std::vector <HEMUIRFEntry> func_tb;
public:
	size_t add_func(const HEMUIRFEntry& func);
	size_t search_idx(std::string fname);
	HEMUIRFEntry get_fe(size_t idx);
};

/*
 * HEMU IR Function Instance
 */
struct HEMUIRFIns {
	size_t func_idx;
	size_t cur_line;
	HEMUIRTempTb temp_tb;
	char* stack_ptr;
	char* base_ptr;
};

/*
 * HEMU IR Function Instance Table
 */
class HEMUIRFInsTb {
private:
	std::vector <HEMUIRFIns> fun_ins;
public:
	size_t fins_size();
	size_t push_func_ins(const HEMUIRFIns& func_ins);
	bool pop_func_ins();
	HEMUIRFIns get_fins_top();
	HEMUIRFIns get_fins_next();
	HEMUIRVar get_cur_fins_var(const HEMUIROpnd& opnd);
	bool set_cur_fins_line(size_t lineno);
	size_t set_cur_fins_temp_tb(size_t temp_idx, const HEMUIRVar& val);
	size_t set_next_fins_temp_tb(size_t temp_idx, const HEMUIRVar& val);
	size_t set_cur_fins_temp_tb(const HEMUIROpnd& opnd, const HEMUIRVar& val);
};

// `cond` for `icmp or fcmp` instruction
enum class CMPCond {
	EQ, NE, GT, GE, LT, LE
};



/*
 * HEMU IR Inst
 */
class HEMUIRInst {
public:
	bool hdb_break;
	HEMUIROper oper;
	// Source type, only scalar except for `ALLOCA`
	HEMUIRType type;
	bool call_dest; // if ret val
	HEMUIROpnd dest;

	// Source 0
	union {
		HEMUIROpnd cond;	// `BRc`
		HEMUIROpnd op1;		// `fneg`, `add`, ..., `xor`, `icmp`, `fcmp
		HEMUIROpnd pointer;	// `load`, `store`
		size_t funcidx;		// `call` // function index
	};
	// Source 1
	union
	{
		HEMUIROpnd value;	//`ret`, `store`
		HEMUIROpnd ifture;	//`BRc`
		HEMUIROpnd brdest;	//`BRu`
		HEMUIROpnd op2;		//`add`, ..., `xor`, `icmp`, `fcmp`
	};
	// Source 2
	union {
		HEMUIROpnd iffalse;		//`BRc`
		uint32_t alignment;		// for `alloca`, only power of 2
		CMPCond cmpcond;			// `icmp`, `fcmp`

	};
	// Source 3
	std::string func_name;
	std::vector<HEMUIROpnd> fargs;	// `call`

public:
	bool exec_nop();

	bool exec_call();
	bool exec_ret();
	bool exec_br();

	bool exec_alloca();
	bool exec_load();
	bool exec_store();

	bool exec_cmp();
	bool exec_fneg();
	bool exec_calc();

	bool exec_line();

};

/*
 * HEMU IR Code
 */
class HEMUIRCode {
private:
	std::vector<HEMUIRInst> insts;
public:
	bool run_status;
	bool hemu_hdb;
	bool hemu_err;
	size_t cur_inst_idx;
public:
	HEMUIRCode() {};
	HEMUIRCode(std::istream& stream, bool debug = false);
	HEMUIRInst get_inst(size_t idx);
	bool set_cur_line(size_t lineno);

	HEMUIRVar executor(bool debug = false);

	void err(bool ass, const char* info = nullptr);
	void hdb();
	void hdb_break(const std::vector<std::string> &toks);
	void hdb_print(const std::vector<std::string> &toks);
	void hdb_x(const std::vector<std::string> &toks);
	void hdb_list(const std::vector<std::string> &toks);


};


#endif